package cn.snow.iot.module.warning.service.config;


import cn.hutool.json.JSONArray;
import cn.snow.iot.framework.common.pojo.CommonResult;
import cn.snow.iot.module.warning.controller.admin.config.vo.WarningDetailVO;
import cn.snow.iot.module.warning.controller.admin.config.vo.WarningPageVO;
import cn.snow.iot.module.warning.controller.admin.config.vo.WarningSaveVO;
import cn.snow.iot.module.warning.controller.admin.home.vo.EquipmentRelateWarningVO;
import cn.snow.iot.module.warning.controller.admin.home.vo.WarningConfigStatusVO;
import cn.snow.iot.module.warning.dal.dataobject.config.ConfigActionDO;
import cn.snow.iot.module.warning.dal.dataobject.config.WarningDO;
import cn.snow.iot.module.warning.dal.dataobject.rule.WarningRuleDO;
import cn.snow.iot.module.warning.dal.mysql.config.WarningMapper;
import cn.snow.iot.module.warning.ekuiperClient.WarningClient;
import cn.snow.iot.module.warning.enums.api.config.vo.ConfigCountVO;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Service
public class WarningServiceImpl extends ServiceImpl<WarningMapper, WarningDO> implements IWarningService {

    private static final AtomicInteger counter = new AtomicInteger(0);

    @Value("${ekuiper.url:}")
    private String url;

    @Value("${ekuiper.startSql:}")
    private String startSql;

    @Value("${ekuiper.ruleSql:}")
    private String ruleSql;

    @Value("${ekuiper.endSql:}")
    private String endSql;

    @Value("${ekuiper.actions.server:}")
    private String actionsServer;

    @Value("${ekuiper.actions.topic:}")
    private String actionsTopic;

    @Value("${ekuiper.actions.username:}")
    private String actionsUsername;

    @Value("${ekuiper.actions.password:}")
    private String actionsPassword;

    @Value("${ekuiper.actions.sendSingle:}")
    private Boolean sendSingle;


    @Resource
    private IConfigActionService configActionService;

    @Override
    public IPage<WarningDO> selectWarningPage(WarningPageVO warningPageVO) {
        IPage<WarningDO> page = new Page<>(warningPageVO.getPageNo(), warningPageVO.getPageSize());
        return baseMapper.selectProductPage(page, warningPageVO);
    }

    @Override
    public CommonResult<WarningDO> saveWarningConfig(WarningSaveVO warningSaveVO) {
         WarningClient warningClient = new WarningClient();
        WarningDO warningDO = new WarningDO();
        BeanUtils.copyProperties(warningSaveVO, warningDO);
        boolean status = true;

        // 判断为新增还是更新
        if (warningDO.getId() == null) {
            String ruleId = generateUniqueId();
            WarningRuleDO warningRuleDO = warningSaveVO.getWarningRuleDO();

            String sql = startSql + getStartSql(warningSaveVO) + " " + ruleId + " " + ruleSql + " " + timeType(warningSaveVO);

            warningDO.setRuleSql(sql);
            warningDO.setRuleCode(warningRuleDO.getId());
            warningDO.setRuleThreshold(warningRuleDO.getRuleThreshold());
            warningDO.setRuleExpression(warningRuleDO.getRuleExpression());
            warningDO.setRuleCondition(warningRuleDO.getRuleCondition());
            warningDO.setComputeType(warningRuleDO.getComputeType());
            warningDO.setTimeType(warningRuleDO.getTimeType());
            warningDO.setTimeValue(warningRuleDO.getTimeValue());
            JSONObject jsonObject = new JSONObject();
            warningDO.setRuleId(ruleId);
            jsonObject.put("id", ruleId);
            jsonObject.put("name", warningSaveVO.getWarningName());
            jsonObject.put("sql", sql);
            settingsAction(jsonObject);

            // 新增配置
            status = warningClient.ruleCreation(jsonObject, url);
            if (status) {
                // 保存配置
                warningDO.setIsEnable(1);
                save(warningDO);
                List<ConfigActionDO> actionDOList = warningSaveVO.getActionDOList();
                actionDOList.forEach(item -> {
                    item.setId(null);
                    item.setConfigId(warningDO.getId());
                });
                configActionService.saveBatch(actionDOList);
                return CommonResult.success(warningSaveVO);
            } else {
                return CommonResult.error(400, "新增失败！");
            }
        }
        else {
            // 更新操作
            WarningDO warning = getById(warningSaveVO.getId());
            String ruleId = warningSaveVO.getRuleId();
            if (warningSaveVO.getRuleSql() != null) {
                WarningRuleDO warningRuleDO = warningSaveVO.getWarningRuleDO();

                String sql = startSql + getStartSql(warningSaveVO) + " " + ruleId + " " + ruleSql + " " + timeType(warningSaveVO);

                if (!warning.getRuleSql().equals(sql)) {
                    warningDO.setRuleSql(sql);
                    warningDO.setRuleCode(warningRuleDO.getId());
                    warningDO.setRuleThreshold(warningRuleDO.getRuleThreshold());
                    warningDO.setRuleExpression(warningRuleDO.getRuleExpression());
                    warningDO.setRuleCondition(warningRuleDO.getRuleCondition());
                    warningDO.setComputeType(warningRuleDO.getComputeType());
                    warningDO.setTimeType(warningRuleDO.getTimeType());
                    warningDO.setTimeValue(warningRuleDO.getTimeValue());
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("id", ruleId);
                    jsonObject.put("name", warningSaveVO.getWarningName());
                    jsonObject.put("sql", sql);
                    settingsAction(jsonObject);
                    status = warningClient.ruleUpdate(jsonObject, url);
                }
            }
            if (status) {
                updateById(warningDO);
                List<ConfigActionDO> actionDOList = warningSaveVO.getActionDOList();
                actionDOList.forEach(item -> {
                    item.setConfigId(warningSaveVO.getId());
                    item.setId(null);
                });
                configActionService.updateAction(warningSaveVO);
                return CommonResult.success(warningSaveVO);
            } else {
                return CommonResult.error(400, "修改失败！");
            }
        }

    }

    @Override
    public Boolean deleteWarningConfig(Long id) {
        boolean isSuccess = false;
        WarningClient warningClient = new WarningClient();
        WarningDO warningDO = getById(id);
        isSuccess = warningClient.ruleDelete(warningDO.getRuleId(), url);
        if (isSuccess) {
            removeById(id);
            configActionService.deleteByConfigId(id);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public CommonResult<WarningDO> updateStatus(WarningDO warningDO) {
        boolean isSuccess = false;
        WarningClient warningClient = new WarningClient();
        WarningDO warning = getById(warningDO.getId());
        if (warningDO.getIsEnable() == 1) {
            warning.setIsEnable(1);
            isSuccess = warningClient.ruleStart(warning.getRuleId(), url);
        } else {
            warning.setIsEnable(0);
            isSuccess = warningClient.ruleStop(warning.getRuleId(), url);
        }
        if (isSuccess) {
            saveOrUpdate(warning);
            return CommonResult.success(warningDO);
        } else {
            return CommonResult.error(400, "更改失败！");
        }
    }

    /**
     * 根据告警id获取告警配置信息和动作列表，此方法仅供告警事件调用
     * @param id
     * @return
     */
    @Override
    public WarningDetailVO selectWarningById(Long id) {
        WarningDetailVO warningDetailVO = new WarningDetailVO();
        WarningDO warningDO = getByRuleId(id);
        BeanUtils.copyProperties(warningDO, warningDetailVO);
        List<ConfigActionDO> actionDOList = configActionService.getActionByConfigId(warningDO.getId());
        warningDetailVO.setActionDOList(actionDOList);
        return warningDetailVO;
    }

    @Override
    public CommonResult<WarningDetailVO> getWarningById(Long id) {
        WarningDetailVO warningDetailVO = new WarningDetailVO();
        WarningDO warningDO = getById(id);
        BeanUtils.copyProperties(warningDO, warningDetailVO);
        List<ConfigActionDO> actionDOList = configActionService.getActionByConfigId(id);
        warningDetailVO.setActionDOList(actionDOList);
        // 跨服务调用设备和点位信息
        return CommonResult.success(warningDetailVO);
    }

    @Override
    public Boolean restart(Long id) {
        WarningDO warning = getById(id);
        boolean isSuccess = false;
        WarningClient warningClient = new WarningClient();
        isSuccess = warningClient.ruleRestart(warning.getRuleId(), url);
        return isSuccess;
    }

    private void settingsAction(JSONObject jsonObject) {
        JSONArray actionsArray = new JSONArray();
        JSONObject mqttAction = new JSONObject();
        JSONObject mqttObject = new JSONObject();
        mqttObject.put("server", actionsServer);
        mqttObject.put("topic", actionsTopic);
        if (actionsUsername.isEmpty()) {
            mqttObject.put("username", "");
        } else {
            mqttObject.put("username", actionsUsername);
        }
        if (actionsPassword.isEmpty()) {
            mqttObject.put("password", "");
        } else {
            mqttObject.put("password", actionsPassword);
        }
        mqttObject.put("sendSingle", sendSingle);
        mqttAction.put("mqtt", mqttObject);
        actionsArray.put(mqttAction);
        jsonObject.put("actions", actionsArray);
    }
    public static String generateUniqueId() {
        long timestamp = System.currentTimeMillis();
        int randomNumber = (int) (Math.random() * 1000);
        int uniqueId = counter.getAndIncrement();

        String id = String.format("%d%03d%04d", timestamp, randomNumber, uniqueId);

        if (id.length() > 15) {
            id = id.substring(0, 15);
        } else if (id.length() < 15) {
            id = String.format("%-15s", id).replace(' ', '0');
        }
        return id;
    }

    @Override
    public WarningDO getByRuleId(Long id) {
        return baseMapper.getByRuleId(id);
    }

    @Override
    public List<ConfigCountVO> getWarningCountByEquipmentId(List<Long> ids) {
        List<ConfigCountVO> warningCountByEquipmentId = baseMapper.getWarningCountByEquipmentId(ids);
        return warningCountByEquipmentId;
    }

    @Override
    public Integer getConfifCount() {
        return baseMapper.getConfifCount();
    }

    @Override
    public List<EquipmentRelateWarningVO> getEquipmentRelateWarning() {
        return baseMapper.getEquipmentRelateWarning();
    }

    @Override
    public boolean deleteByEquipmentId(Long id) {
        // 根据设备id查询出所有关联的告警信息
        List<WarningDO> warningDOList = baseMapper.selectByEquipmentId(id);
        boolean status = deleteBatchConfig(warningDOList);
        return status;
    }

    @Override
    public List<WarningConfigStatusVO> getStatusCount() {
        return baseMapper.getStatusCount();
    }

    public boolean deleteBatchConfig(List<WarningDO> warning) {
        WarningClient warningClient = new WarningClient();
        boolean isSuccess = false;
        boolean status = true;
        for (WarningDO warningDO : warning) {
            isSuccess = warningClient.ruleDelete(warningDO.getRuleId(), url);
            if (isSuccess) {
                removeById(warningDO.getId());
                configActionService.deleteByConfigId(warningDO.getId());
                status =  true;
            } else {
                status =  false;
            }
        }
        return status;

    }

    /**
     * 根据条件参数拼接相关条件
     * @param warningSaveVO
     * @return
     */
    public String selectType(WarningSaveVO warningSaveVO) {
        WarningRuleDO warningDO = warningSaveVO.getWarningRuleDO();
        // 获取判断条件
        Integer conditionType = warningDO.getConditionType();
        String ruleThreshold;
        String[] split;
        if (!warningDO.getProcessingType().equals("string")) {
            switch (conditionType) {
                case 1:
                    return "=" + " " + warningDO.getRuleThreshold();
                case 2:
                    return "!=" + " " + warningDO.getRuleThreshold();
                case 3:
                    return ">" + " " + warningDO.getRuleThreshold();
                case 4:
                    return ">=" + " " + warningDO.getRuleThreshold();
                case 5:
                    return "<" + " " + warningDO.getRuleThreshold();
                case 6:
                    return "<=" + " " + warningDO.getRuleThreshold();
                case 7:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "BETWEEN" + " " + split[0] + " AND " + split[1];
                case 8:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "NOT BETWEEN" + " " + split[0] + " AND " + split[1];
                case 9:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "IN" + " (" + split[0] + " , " + split[1] + ")";
                case 10:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "NOTIN" + " (" + split[0] + " , " + split[1] + ")";
                default:
                    return null;
            }
        } else {
            switch (conditionType) {
                case 1:
                    return "=" + " '" + warningDO.getRuleThreshold() + "'";
                case 2:
                    return "!=" + " '" + warningDO.getRuleThreshold() + "'";
                case 3:
                    return ">" + " '" + warningDO.getRuleThreshold() + "'";
                case 4:
                    return ">=" + " '" + warningDO.getRuleThreshold() + "'";
                case 5:
                    return "<" + " '" + warningDO.getRuleThreshold() + "'";
                case 6:
                    return "<=" + " '" + warningDO.getRuleThreshold() + "'";
                case 7:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "BETWEEN" + " '" + split[0] + "' AND '" + split[1] + "'";
                case 8:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "NOT BETWEEN" + " '" + split[0] + "' AND '" + split[1] + "'";
                case 9:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "IN" + " ('" + split[0] + "' , '" + split[1] + "')";
                case 10:
                    ruleThreshold = warningDO.getRuleThreshold();
                    split = ruleThreshold.split("~");
                    return "NOTIN" + " ('" + split[0] + "' , '" + split[1] + "')";
                default:
                    return null;
            }
        }
    }

    /**
     * 计算类型 - 聚合函数
     * @param warningSaveVO
     * @return
     */
    public String computeType(WarningSaveVO warningSaveVO) {
        WarningRuleDO warningDO = warningSaveVO.getWarningRuleDO();
        switch (warningDO.getComputeType()) {
            case 1:
                return " " + warningSaveVO.getPointCode() + " ";
            case 2:
                return " lag(" + warningSaveVO.getPointCode() + ") " + "OVER (PARTITION BY deviceCode = "+ "\"" + warningSaveVO.getEquipmentCode() + "\"" +")";
            default:
                return "";
        }
    }

    /**
     * 计算类型 - 聚合函数
     * @param warningSaveVO
     * @return
     */
    public String getStartSql(WarningSaveVO warningSaveVO) {
        WarningRuleDO warningDO = warningSaveVO.getWarningRuleDO();
        switch (warningDO.getComputeType()) {
            case 1:
                return " " + warningSaveVO.getPointCode() + " AS value, thetime, ";
            case 2:
                return " lag(" + warningSaveVO.getPointCode() + ") " + "OVER (PARTITION BY deviceCode = "+ "\"" + warningSaveVO.getEquipmentCode() + "\"" +")" +" AS value, thetime, ";
            default:
                return " ";
        }
    }

    public String timeType(WarningSaveVO warningSaveVO) {
        return "WHERE deviceCode = " + "\"" +  warningSaveVO.getEquipmentCode() + "\"" + " AND " + computeType(warningSaveVO) + selectType(warningSaveVO);
    }
}
